package com.ra.pay.weixin;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.net.URLEncoder;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.concurrent.ExecutionException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.math.NumberUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.espirit.weixin.core.WeixinConfig;
import com.espirit.weixin.core.WeixinUtil;
import com.googlecode.cswish.struts.spring.ObjectFactory;

/**
 * 微信支付工具类
 * 
 * @author colin
 * @date 2014年10月24日
 */
public class WeixinPayUtil {
	
	private static final Log logger = LogFactory.getLog(WeixinPayUtil.class);

	private static final String CHARSET = "UTF-8";
	
	private HashMap<String, String> parameters = new HashMap<String, String>();
	
	WeixinUtil weixinUtil = ObjectFactory.getInstance().getBean(WeixinUtil.class, true);
	
	// 微信统一支付接口
	private static final String UNIFIED_ORDER_URL = "https://api.mch.weixin.qq.com/pay/unifiedorder";
	
	// 微信企业转账接口
	private static final String ENTERPRISE_TRANSFERS_URL = "https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers";
	
	// 微信发货通知
	private static final String DELIVERNOTIFY_URL = "";
	
	private static Object Server;
	
	/**
	 * 把对象转换成字符串
	 * @param obj
	 * @return String 转换成字符串,若对象为null,则返回空字符串.
	 */
	public static String toString(Object obj) {
		if(obj == null){
			return "";
		}
		return obj.toString();
	}
	
	/**
	 * 把对象转换为int数值.
	 * 
	 * @param obj
	 *            包含数字的对象.
	 * @return int 转换后的数值,对不能转换的对象返回0。
	 */
	public static int toInt(Object obj) {
		int a = 0;
		try {
			if (obj != null)
				a = Integer.parseInt(obj.toString());
		} catch (Exception e) {
			logger.error("对象转换为int数值时出问题："+obj.toString());
		}
		return a;
	}
	
	/**
	 * 获取当前时间 yyyyMMddHHmmss
	 * @return String
	 */ 
	public static String getCurrTime() {
		Date now = new Date();
		SimpleDateFormat outFormat = new SimpleDateFormat("yyyyMMddHHmmss");
		String s = outFormat.format(now);
		return s;
	}
	
	/**
	 * 获取当前日期 yyyyMMdd
	 * @param date
	 * @return String
	 */
	public static String formatDate(Date date) {
		SimpleDateFormat formatter = new SimpleDateFormat("yyyyMMdd");
		String strDate = formatter.format(date);
		return strDate;
	}
	
	/**
	 * 取出一个指定长度大小的随机正整数.
	 * 
	 * @param length
	 *            int 设定所取出随机数的长度。length小于11
	 * @return int 返回生成的随机数。
	 */
	public static int buildRandom(int length) {
		int num = 1;
		double random = Math.random();
		if (random < 0.1) {
			random = random + 0.1;
		}
		for (int i = 0; i < length; i++) {
			num = num * 10;
		}
		return (int) ((random * num));
	}
	
	/**
	 * 8位日期+4位随机数
	 * @return
	 */
	public static String getRandomNum(){
		String currTime = WeixinPayUtil.getCurrTime();
		//8位日期
		String strTime = currTime.substring(8, currTime.length());
		//四位随机数
		String strRandom = WeixinPayUtil.buildRandom(4) + "";
		//12位序列号,可以自行调整。
		String strReq = strTime + strRandom;
		return strReq;
	}
	
	
	/**
	 * 获取编码字符集
	 * @param request
	 * @param response
	 * @return String
	 */
	public static String getCharacterEncoding(HttpServletRequest request,
			HttpServletResponse response) {
		
		if(null == request || null == response) {
			return "utf-8";
		}
		
		String enc = request.getCharacterEncoding();
		if(null == enc || "".equals(enc)) {
			enc = response.getCharacterEncoding();
		}
		
		if(null == enc || "".equals(enc)) {
			enc = "utf-8";
		}
		
		return enc;
	}
	
	/**
	 * 获取unix时间，从1970-01-01 00:00:00开始的秒数
	 * @param date
	 * @return long
	 */
	public static long getUnixTime(Date date) {
		if( null == date ) {
			return 0;
		}
		return date.getTime()/1000;
	}
	
	/**
	 * 时间转换成字符串
	 * @param date 时间
	 * @param formatType 格式化类型
	 * @return String
	 */
	public static String date2String(Date date, String formatType) {
		SimpleDateFormat sdf = new SimpleDateFormat(formatType);
		return sdf.format(date);
	}
	
	
	
	
	
	/**
     * 参与 paySign 签名的字段包括：appid、timestamp、noncestr、package 以及 appkey。
     * 这里 signType 并不参与签名微信的Package参数
     * @param params
     * @return
     * @throws UnsupportedEncodingException 
     */
    public static String getPackage(Map<String, String> params) throws UnsupportedEncodingException {
        // 公共参数
//        params.put("bank_type", "WX");
//        params.put("attach", "yongle");
//        params.put("partner", partnerId);
//        params.put("notify_url", notifyUrl);
//        params.put("input_charset", "UTF-8");
//        return packageSign(params, PartnerKey);
    	return null;
    }

    /**
     * 构造签名
     * @param params
     * @param encode
     * @return
     * @throws UnsupportedEncodingException 
     */
    public static String createSign(Map<String, String> params, boolean encode) throws UnsupportedEncodingException {
        Set<String> keysSet = params.keySet();
        Object[] keys = keysSet.toArray();
        Arrays.sort(keys);
        StringBuffer temp = new StringBuffer();
        boolean first = true;
        for (Object key : keys) {
            if (first) {
                first = false;
            } else {
                temp.append("&");
            }
            temp.append(key).append("=");
            Object value = params.get(key);
            String valueString = "";
            if (null != value) {
                valueString = value.toString();
            }
            if (encode) {
                temp.append(URLEncoder.encode(valueString, "UTF-8"));
            } else {
                temp.append(valueString);
            }
        }
        return temp.toString();
    }

    /**
     * 构造package
     * @param params
     * @param paternerKey
     * @return
     * @throws UnsupportedEncodingException 
     */
    public static String packageSign(Map<String, String> params, String paternerKey) throws UnsupportedEncodingException {
        String string1 = createSign(params, false);
        String stringSignTemp = string1 + "&key=" + paternerKey;
        return DigestUtils.md5Hex(stringSignTemp).toUpperCase();
    }

    /**
     * 支付签名
     * @param timestamp
     * @param noncestr
     * @param packages
     * @return
     * @throws UnsupportedEncodingException 
     */
    public static String paySign(String timestamp, String noncestr,String packages) throws UnsupportedEncodingException {
//        Map<String, String> paras = new HashMap<String, String>();
//        paras.put("appid", AppId);
//        paras.put("timestamp", timestamp);
//        paras.put("noncestr", noncestr);
//        paras.put("package", packages);
//        paras.put("appkey", paySignKey);
//        // appid、timestamp、noncestr、package 以及 appkey。
//        String string1 = createSign(paras, false);
//        String paySign = DigestUtils.shaHex(string1);
//        return paySign;
    	return null;
    }
    
    /**
     * 支付回调校验签名
     * @param timestamp
     * @param noncestr
     * @param openid
     * @param issubscribe
     * @param appsignature
     * @return
     * @throws UnsupportedEncodingException 
     */
    public static boolean verifySign(long timestamp,
            String noncestr, String openid, int issubscribe, String appsignature) throws UnsupportedEncodingException {
//        Map<String, String> paras = new HashMap<String, String>();
//        paras.put("appid", AppId);
//        paras.put("appkey", paySignKey);
//        paras.put("timestamp", String.valueOf(timestamp));
//        paras.put("noncestr", noncestr);
//        paras.put("openid", openid);
//        paras.put("issubscribe", String.valueOf(issubscribe));
//        // appid、appkey、productid、timestamp、noncestr、openid、issubscribe
//        String string1 = createSign(paras, false);
//        String paySign = DigestUtils.shaHex(string1);
//        return paySign.equalsIgnoreCase(appsignature);
    	return false;
    }
    
    /**
     * 发货通知签名
     * @param paras
     * @return
     * @throws UnsupportedEncodingException
     * 
     * @参数 appid、appkey、openid、transid、out_trade_no、deliver_timestamp、deliver_status、deliver_msg；
     */
    private static String deliverSign(Map<String, String> paras) throws UnsupportedEncodingException {
//        paras.put("appkey", paySignKey);
//        String string1 = createSign(paras, false);
//        String paySign = DigestUtils.shaHex(string1);
//        return paySign;
    	return null;
    }
    
    
    /**
     * 发货通知
     * @param access_token
     * @param openid
     * @param transid
     * @param out_trade_no
     * @return
     * @throws IOException 
     * @throws NoSuchProviderException 
     * @throws NoSuchAlgorithmException 
     * @throws KeyManagementException 
     * @throws InterruptedException 
     * @throws ExecutionException 
     */
    public static boolean delivernotify(String access_token, String openid, String transid, String out_trade_no) throws IOException, ExecutionException, InterruptedException {
//        Map<String, String> paras = new HashMap<String, String>();
//        paras.put("appid", AppId);
//        paras.put("openid", openid);
//        paras.put("transid", transid);
//        paras.put("out_trade_no", out_trade_no);
//        paras.put("deliver_timestamp", (System.currentTimeMillis() / 1000) + "");
//        paras.put("deliver_status", "1");
//        paras.put("deliver_msg", "ok");
//        // 签名
//        String app_signature = deliverSign(paras);
//        paras.put("app_signature", app_signature);
//        paras.put("sign_method", "sha1");
//        String json = HttpKit.post(DELIVERNOTIFY_URL.concat(access_token), JSONObject.toJSONString(paras));
//        if (StringUtils.isNotBlank(json)) {
//            JSONObject object = JSONObject.parseObject(json);
//            if (object.containsKey("errcode")) {
//                int errcode = object.getIntValue("errcode");
//                return errcode == 0;
//            }
//        }
        return false;
    }
    
    /**
     * 判断是否来自微信, 5.0 之后的支持微信支付
     * @param request
     * @return
     */
 	public static boolean isWeiXin(HttpServletRequest request) {
 		String userAgent = request.getHeader("User-Agent");
 		if (StringUtils.isNotBlank(userAgent)) {
 			Pattern p = Pattern.compile("MicroMessenger/(\\d+).+");
 			Matcher m = p.matcher(userAgent);
 			String version = null;
 			if(m.find()){
 				version = m.group(1);
 			}
 			return (null != version && NumberUtils.toInt(version) >= 5); 
 		}
 		return false;
 	}
 	
 	public static void main(String[] args) throws Exception{
 		payTest();
	}
 	
 	public static String payTest(){
 		WeixinPayUtil weixinPayUtil = new WeixinPayUtil();
 		WeixinGoods goods = new WeixinGoods();
 		goods.setAttach("this is attach!");
 		goods.setBody("8月份房租");
 		goods.setGoodsTag("");
 		goods.setOutTradeNo("2014-11-04-124");
 		goods.setTotalFee("5000");
 		String str = "";
		try {
			str = weixinPayUtil.UnifiedOrder(goods, null);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
 		System.out.println("  -->"+str);
 		return str;
 	}
 	
 	public String UnifiedOrder(WeixinGoods goods,HttpServletRequest request) throws Exception{
 		//String ip = getIp(request);
// 		String ip = "10.198.0.126";
//		// 组装统一支付接口json字符串
//		String packageJSON = CreateNativePackage(goods,ip);
//		System.out.println("-------------------------------");
//		System.out.println(packageJSON);
//		System.out.println("-------------------------------");
//		// 调用统一支付接口
//		String resultCodeUrl = null;
//		HttpPost post = HttpClientManager.getPostMethod(UNIFIED_ORDER_URL);
//		HttpClient client = HttpClientManager.getSSLInstance();
//		
//		try {
//			post.setEntity(new StringEntity(packageJSON, CHARSET));
//			HttpResponse response = client.execute(post);
//			String json = EntityUtils.toString(response.getEntity(), CHARSET);
//			System.out.println("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
//			System.out.println(json);
//			System.out.println("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
//			if (logger.isDebugEnabled()) {
//				logger.debug("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
//				logger.debug(json);
//				logger.debug("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
//			}
//			
//			PayResult payResult = weixinUtil.jsonToObject(json, PayResult.class);
//
//			if (null != payResult) {
//				
//				if ("FAIL".equals(payResult.getReturn_code())) {
//					logger.error("统一支付接口提交时出现【通信】异常.错误描述："+ payResult.getReturn_msg());
//				}else if("FAIL".equals(payResult.getResult_code())){
//					logger.error("统一支付接口提交时出现【业务】异常.错误代码："+ payResult.getErr_code()+",错误描述："+payResult.getErr_code_des());
//				}else if(payResult.getCode_url() != null){
//					resultCodeUrl = payResult.getCode_url();
//				}
//				
//			}
//		} catch (IOException e) {
//			logger.error("调用统一支付接口出现异常." + e.getMessage(), e);
//		}
//		return resultCodeUrl;
 		return null;
	}
 	
 	/**
 	* 生成原生支付请求xml
 	* 
 	*<pre>
 	*	<xml>
 	*		<appid>wx2421b1c4370ec43b</appid>
 	*		<mch_id><![CDATA[11111222]]></mch_id>
 	*		<body><![CDATA[JSAPI 支付测试 ]]></body>
 	*		<device_info>1000</device_info>
 	*		<mch_id>10000100</mch_id>
 	*		<nonce_str>b927722419c52622651a871d1d9ed8b2</nonce_str>
 	*		<notify_url>http://wxpay.weixin.qq.com/pub_v2/pay/notify.php</notify_url>
 	*		<out_trade_no>1405713376</out_trade_no>
 	*		<spbill_create_ip>127.0.0.1</spbill_create_ip>
 	*		<total_fee>1</total_fee>
 	*		<trade_type>JSAPI</trade_type>
 	*		<sign><![CDATA[3CA89B5870F944736C657979192E1CF4]]></sign>
 	*	</xml>
 	*</pre>
 	*/
 	public String CreateNativePackage(WeixinGoods goods,String ip)  {
 		HashMap<String, String> nativeObj = new HashMap<String, String>();
 		// 公众账号ID
// 		nativeObj.put("appid", AppId);
// 		// 商户号
// 		nativeObj.put("mch_id", mchId);
// 		// 终端ip
// 		nativeObj.put("spbill_create_ip", ip);
// 		// 随机字符串
// 	 	nativeObj.put("nonce_str", CommonUtil.CreateNoncestr());
// 	 	// 商户订单号
// 	 	nativeObj.put("out_trade_no", goods.getOutTradeNo());
// 	 	// 商品描述
// 	 	nativeObj.put("body", goods.getBody());
// 	 	// 总金额
// 	 	nativeObj.put("total_fee", goods.getTotalFee());
// 	 	// 通知地址
// 	 	nativeObj.put("notify_url", notifyUrl);
// 	 	// 交易类型
// 	 	nativeObj.put("trade_type", "NATIVE");
// 	 	
// 	 	// 签名
// 	 	try {
//			nativeObj.put("sign", packageSign(nativeObj,PartnerKey));
//		} catch (UnsupportedEncodingException e) {
//			throw new RuntimeException("统一支付请求中的签名出现问题",e);
//		}
// 		return CommonUtil.ArrayToXml(nativeObj);
 		return null;
 	}
 	
	private Boolean CheckCftParameters() {
		if (parameters.get("bank_type") == "" || parameters.get("body") == ""
				|| parameters.get("partner") == ""
				|| parameters.get("out_trade_no") == ""
				|| parameters.get("total_fee") == ""
				|| parameters.get("fee_type") == ""
				|| parameters.get("notify_url") == null
				|| parameters.get("spbill_create_ip") == ""
				|| parameters.get("input_charset") == "") {
			return false;
		}
		return true;
	}
	
	/**
	 * 获取ip
	 * @param request
	 * @return
	 */
	public static String getIp(HttpServletRequest request) {
		if (request == null)
			return "";
		String ip = request.getHeader("X-Requested-For");
		if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
			ip = request.getHeader("X-Forwarded-For");
		}
		if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
			ip = request.getHeader("Proxy-Client-IP");
		}
		if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
			ip = request.getHeader("WL-Proxy-Client-IP");
		}
		if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
			ip = request.getHeader("HTTP_CLIENT_IP");
		}
		if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
			ip = request.getHeader("HTTP_X_FORWARDED_FOR");
		}
		if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
			ip = request.getRemoteAddr();
		}
		return ip;
	}
	
	/**
	 * 企业转账
	 * @param req
	 * @param res
	 * @param weixinConfig
	 * @param transfer
	 * @return
	 */
	public static String enterprisePay(HttpServletRequest req,HttpServletResponse res,WeixinConfig weixinConfig, EnterpriseTransfer transfer) {
		
		//转账的相关信息
		String partner_trade_no = transfer.getPartner_trade_no();
		String finalmoney = transfer.getAmount();
		String desc = transfer.getDesc();
		String openid = transfer.getOpenid();
		
		//商户相关资料 
	    String appid = weixinConfig.getAppId();
	    String appsecret = weixinConfig.getSecret();
	    String partner = weixinConfig.getPartnerId();
	    String partnerkey = weixinConfig.getPartnerKey();
	    
	    if (logger.isDebugEnabled()) {
			logger.debug("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
			logger.debug("appid:" + appid);
			logger.debug("appsecret:" + appsecret);
			logger.debug("partner:" + partner);
			logger.debug("partnerkey:" + partnerkey);
			logger.debug("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
		}
	    
		//商户号
		String mch_id = partner;
		//随机数 
		String nonce_str = WeixinPayUtil.getRandomNum();
		
		//订单生成的机器 IP
		String spbill_create_ip = req.getRemoteAddr();
		String check_name = "NO_CHECK";
		
		SortedMap<String, String> packageParams = new TreeMap<String, String>();
		packageParams.put("mch_appid", appid);  
		packageParams.put("mchid", mch_id);  
		packageParams.put("nonce_str", nonce_str);  
		packageParams.put("desc", desc);  
		packageParams.put("partner_trade_no", partner_trade_no);  
		packageParams.put("amount", finalmoney);  
		packageParams.put("spbill_create_ip", spbill_create_ip);  
		packageParams.put("check_name", check_name);  
		packageParams.put("openid", openid);  
		
		RequestHandler reqHandler = new RequestHandler(req, res);
		reqHandler.init(appid, appsecret, partnerkey);
		String sign = reqHandler.createSign(packageParams);
		
		String xmlParam="<xml>"+
				"<mch_appid>"+appid+"</mch_appid>"+
				"<mchid>"+mch_id+"</mchid>"+
				"<nonce_str>"+nonce_str+"</nonce_str>"+
				"<sign>"+sign+"</sign>"+
				"<desc><![CDATA["+desc+"]]></desc>"+
				"<partner_trade_no>"+partner_trade_no+"</partner_trade_no>"+
				"<spbill_create_ip>"+spbill_create_ip+"</spbill_create_ip>"+
				"<amount>"+finalmoney+"</amount>"+
				"<check_name>"+check_name+"</check_name>"+
				"<openid>"+openid+"</openid>"+
				"</xml>";
		String result = new GetWxOrderno().enterprisePay(ENTERPRISE_TRANSFERS_URL, xmlParam);
		return result;
	}
	
	/**
	 * 分转元
	 * @param money
	 * @return
	 */
	public static String convertMoney(String pointsMoney){
		String yuanMoney = "0.00";
		try {
			BigDecimal b1 = new BigDecimal(pointsMoney);
			BigDecimal b2 = new BigDecimal("0.01");
			double bd = b1.multiply(b2).doubleValue();
			BigDecimal result = new BigDecimal(bd);
			result = result.setScale(2, RoundingMode.HALF_UP);
			yuanMoney = result.toString();
		} catch (Exception e) {
			logger.error("微信支付金额出现异常，参数pointsMoney="+pointsMoney,e); 
		}
		return yuanMoney;
	}
 	
}
